package org.kazuhiko.rpg.util;

import static org.lwjgl.opengl.GL11.GL_FLOAT;
import static org.lwjgl.opengl.GL11.glNormalPointer;
import static org.lwjgl.opengl.GL11.glVertexPointer;
import static org.lwjgl.opengl.GL15.GL_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15.GL_STATIC_DRAW;
import static org.lwjgl.opengl.GL15.glBindBuffer;
import static org.lwjgl.opengl.GL15.glBufferData;
import static org.lwjgl.opengl.GL15.glGenBuffers;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.util.HashMap;

import org.kazuhiko.rpg.core.Face;
import org.kazuhiko.rpg.core.Model;
import org.lwjgl.util.vector.Vector2f;
import org.lwjgl.util.vector.Vector3f;
import org.newdawn.slick.opengl.Texture;

public class ObjectLoader
{
	private static HashMap<String, Model> loadedModels = new HashMap<String, Model>();
	
	public static Model loadModel( File f ) throws IOException
	{
		if( loadedModels.containsKey( f.getName() ) )
		{
			System.out.println( "Model was loaded from cache" );
			return loadedModels.get( f.getName() );
		}
		
		BufferedReader reader = new BufferedReader( new FileReader( f ) );
		
		Model m = new Model();
		
		Texture currentTexture = null;
		
		String line;
		
		long start = System.currentTimeMillis();
		
		float smallWidth = 0f, smallHeight = 0f, smallDepth = 0f, bigWidth = 0f, bigHeight = 0f, bigDepth = 0f;
		
		currentTexture = ImagingTools.loadTexture( "res/images/textures/" + f.getName().replace( ".obj", "" ) + ".png" );
		
		while( ( line = reader.readLine() ) != null )
		{
			if( line.startsWith( "v " ) )
			{
				String[] split = line.split( " " );
				
				float x = 0;
				float y = 0;
				float z = 0;
				
				if( split.length == 5 )
				{
					x = Float.valueOf( split[2] );
					y = Float.valueOf( split[3] );
					z = Float.valueOf( split[4] );
				}
				else if ( split.length == 4 )
				{
					x = Float.valueOf( split[1] );
					y = Float.valueOf( split[2] );
					z = Float.valueOf( split[3] );
				}
				
				m.vertices.add( new Vector3f( x, y, z ) );
				
				if( x < smallWidth )
				{
					smallWidth = x;
				}
				if( x > bigWidth )
				{
					bigWidth = x;
				}
				if( y < smallHeight )
				{
					smallHeight = y;
				}
				if( y > bigHeight )
				{
					bigHeight = y;
				}
				if( z < smallDepth )
				{
					smallDepth = z;
				}
				if( z > bigDepth )
				{
					bigDepth = z;
				}
			}
			else if( line.startsWith( "vn " ) ) 
			{
				float x = Float.valueOf( line.split( " " )[1] );
				float y = Float.valueOf( line.split( " " )[2] );
				float z = Float.valueOf( line.split( " " )[3] );
				m.normals.add( new Vector3f( x, y, z ) );
			}
			else if( line.startsWith( "f " ) )
			{
				Vector3f vertexIndices = new Vector3f(	Float.valueOf( line.split( " " )[1].split( "/" )[0] ),
														Float.valueOf( line.split( " " )[2].split( "/" )[0] ),
														Float.valueOf( line.split( " " )[3].split( "/" )[0] ) );
				Vector3f textureIndices = new Vector3f();
				if( !line.split( " " )[1].split( "/" )[1].equals( "" ) )
				{
					System.out.println( line.split( " " )[1].split( "/" )[1].equals( "" ) );
					textureIndices = new Vector3f(	Float.valueOf( line.split( " " )[1].split( "/" )[1] ),
													Float.valueOf( line.split( " " )[2].split( "/" )[1] ),
													Float.valueOf( line.split( " " )[3].split( "/" )[1] ) );
				}
				Vector3f normalIndices = new Vector3f(	Float.valueOf( line.split( " " )[1].split( "/" )[2] ),
														Float.valueOf( line.split( " " )[2].split( "/" )[2] ),
														Float.valueOf( line.split( " " )[3].split( "/" )[2] ) );
				m.faces.add( new Face( vertexIndices, textureIndices, normalIndices, ( ( currentTexture != null )? currentTexture.getTextureID() : 0 ) ) );
			}
			else if( line.startsWith( "vt " ) ) 
			{
				float x = Float.valueOf( line.split( " " )[1] );
				float y = Float.valueOf( line.split( " " )[2] );
				
				m.textVertices.add( new Vector2f( x, y ) );
			}
		}
		
		m.depth = ( bigDepth - smallDepth );
		m.height = ( bigHeight - smallHeight );
		m.width = ( bigWidth - smallWidth );
		
		long end = System.currentTimeMillis();
		
		System.out.println( "Model " + f.getName() + " was loaded in " + ( end - start ) + " milliseconds" );
		
		reader.close();
		
		loadedModels.put( f.getName(), m );
		
		return m;
	}
	
	public static int[] createVBO( Model model )
	{
		int vboVertexHandle = glGenBuffers();
		int vboNormalHandle = glGenBuffers();
		
		FloatBuffer vertices = BufferTools.reserveData( model.faces.size() * 9 );
		FloatBuffer normals = BufferTools.reserveData( model.faces.size() * 9 );
		
		for( Face face : model.faces )
		{
			vertices.put( BufferTools.asFloats( model.vertices.get( (int ) face.vertex.x - 1 ) ) );
			vertices.put( BufferTools.asFloats( model.vertices.get( (int ) face.vertex.y - 1 ) ) );
			vertices.put( BufferTools.asFloats( model.vertices.get( (int ) face.vertex.z - 1 ) ) );
			normals.put( BufferTools.asFloats( model.normals.get( (int ) face.normal.x - 1 ) ) );
			normals.put( BufferTools.asFloats( model.normals.get( (int ) face.normal.y - 1 ) ) );
			normals.put( BufferTools.asFloats( model.normals.get( (int ) face.normal.z - 1 ) ) );
		}
		
		vertices.flip();
		normals.flip();
		
		glBindBuffer( GL_ARRAY_BUFFER, vboVertexHandle );
		glBufferData( GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW );
		glVertexPointer( 3, GL_FLOAT, 0, 0L );
		
		glBindBuffer( GL_ARRAY_BUFFER, vboNormalHandle );
		glBufferData( GL_ARRAY_BUFFER, normals, GL_STATIC_DRAW );
		glNormalPointer( GL_FLOAT, 0, 0L );
		
		glBindBuffer( GL_ARRAY_BUFFER, 0 );
		
		return new int[]
		{
			vboVertexHandle,
			vboNormalHandle
		};
	}
}
